package cc.gpai.data_stru.bag.util;

import java.util.Collections;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

import cc.gpai.data_stru.bag.AbstractBag;
import cc.gpai.data_stru.bag.Bag;
import cc.gpai.data_stru.bag.impl.BagEntry;

public abstract class Bags {

	public static <E> Bag<E> unmodifiableBag(final Bag<E> bag) {
		return new AbstractBag<E>() {
			public long modCount(E e, long count) {
				throw new UnsupportedOperationException();
			}

			public void setCount(E e, long count) {
				throw new UnsupportedOperationException();
			}

			@Override
			public long getCount(Object obj) {
				return bag.getCount(obj);
			}

			@Override
			public Set<E> uniqueSet() {
				return Collections.unmodifiableSet(bag.uniqueSet());
			}

			@Override
			public Set<Entry<E, AtomicLong>> entrySet() {
				if(bag instanceof AbstractBag){
					return Collections.unmodifiableSet(((AbstractBag<E>) bag).entrySet());
				}else{
					throw new UnsupportedOperationException();
				}
			}
		};
	}

	public static <E> Bag<E> synchronizedBag(final Bag<E> bag) {
		return new AbstractBag<E>() {

			@Override
			public long getCount(Object obj) {
				return bag.getCount(obj);
			}

			@Override
			public Set<E> uniqueSet() {
				return Collections.synchronizedSet(bag.uniqueSet());
			}

			@Override
			public Set<Entry<E, AtomicLong>> entrySet() {
				if(bag instanceof AbstractBag){
					return Collections.synchronizedSet(((AbstractBag<E>) bag).entrySet());
				}else{
					throw new UnsupportedOperationException();
				}
			}

			@Override
			public synchronized long modCount(E e, long count) {
				if(bag instanceof AbstractBag){
					return ((Bag<E>) bag).modCount(e, count);
				}else{
					throw new UnsupportedOperationException();
				}
			}

			@Override
			public synchronized void setCount(E e, long count) {
				if(bag instanceof AbstractBag){
					((Bag<E>) bag).setCount(e, count);
				}else{
					throw new UnsupportedOperationException();
				}
			}
		};
	}

	public static <E> Bag<E> singletonBag(final E e) {
		return singletonBag(e, 1);
	}

	public static <E> Bag<E> singletonBag(final E e, final long count) {
		return new AbstractBag<E>() {
			E    ele = e;
			long c   = count;

			public long modCount(E e, long count) {
				if (e == ele) {
					c += count;
					return c;
				} else
					throw new UnsupportedOperationException();
			}

			public void setCount(E e, long count) {
				if (e == ele) {
					c = count;
				} else
					throw new UnsupportedOperationException();
			}

			@Override
			public long getCount(Object obj) {
				return c;
			}

			@Override
			public Set<E> uniqueSet() {
				return Collections.singleton(e);
			}

			@Override
			public Set<Entry<E, AtomicLong>> entrySet() {
				return Collections.singleton((Entry<E, AtomicLong>) new BagEntry<E>(e, count));
			}
		};
	}
}
